package com.mall.util.singleton;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * Your need extend SignletonCache AND impl shutdown0 method
 * @param <K> your is cache Key
 * @param <V> your is cache value
 */
public class SingletonCache<K, V> {
    private static final SingletonCache INSTANCE = new SingletonCache();
    private static final AtomicBoolean CLOSE = new AtomicBoolean(false);
    private static final Logger LOGGER = LoggerFactory.getLogger(SingletonCache.class);

    private final Map<K, V> CACHE;
    private final Map<K, Object> lockers;

    SingletonCache() {
        CACHE = new ConcurrentHashMap<>(2000);
        lockers = new ConcurrentHashMap<>(8);

        /* APP close call */
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            shutdown();
        }));
    }

    public static SingletonCache getINSTANCE() {
        return INSTANCE;
    }

    public int size() {
        return CACHE.size();
    }

    public Optional<V> getIfPresent(K k) {
        return Optional.ofNullable(CACHE.get(k));
    }

    public void shutdown0() {
        CACHE.clear();
        lockers.clear();
    }

    public void register(K k, V v) {
        if(!CACHE.containsKey(k)) {
            synchronized (this) {
                lockers.put(k, new Object());
            }
        }

        final Object monitor = lockers.get(k);
        synchronized (monitor) {
            CACHE.put(k, v);
        }
    }

    public void unregister(K k) {
        final Object monitor = lockers.get(k);
        synchronized (monitor) {
            CACHE.remove(k);
        }
    }

    void shutdown() {
        if(!CLOSE.compareAndSet(false, true)) return;

        String className = this.getClass().getName();
        LOGGER.info("{} do shutdown begin", className);

        shutdown0();

        LOGGER.info("{} do shutdown end", className);
    }
}
